package com.jackstraw.jack_base.oss.minio.util;

import com.jackstraw.jack_base.oss.minio.exception.BadMinioException;
import com.jackstraw.jack_base.util.JsonUtil;
import com.jackstraw.jack_base.util.ObjectUtil;
import com.jackstraw.jack_base.util.SecureUtil;
import com.jackstraw.jack_base.util.StringUtil;
import io.minio.BucketExistsArgs;
import io.minio.ComposeObjectArgs;
import io.minio.ComposeSource;
import io.minio.CopyObjectArgs;
import io.minio.CopySource;
import io.minio.GenericResponse;
import io.minio.GetBucketPolicyArgs;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.ObjectWriteResponse;
import io.minio.PutObjectArgs;
import io.minio.RemoveBucketArgs;
import io.minio.RemoveObjectArgs;
import io.minio.RemoveObjectsArgs;
import io.minio.Result;
import io.minio.SetBucketPolicyArgs;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 文件服务器工具类
 */
@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;


    /*=========================存储桶操作====================================*/

    /**
     * 查看存储bucket是否存在
     *
     * @param bucketName 存储桶名称
     * @return true:存在 false:不存在
     */
    public Boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("判断存储桶[{}]是否存在异常", bucketName);
            throw new BadMinioException("001", "判断存储桶是否存在异常", e);
        }
    }

    /**
     * 创建存储bucket
     * 如果存在，则不在创建
     *
     * @param bucketName 存储桶名称
     */
    public void makeBucket(String bucketName) {
        try {
            if (this.bucketExists(bucketName)) {
                return;
            }
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("创建存储桶[{}]异常", bucketName);
            throw new BadMinioException("002", "创建存储桶异常", e);
        }
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储桶名称
     */
    public void removeBucket(String bucketName) {
        try {
            if (!this.bucketExists(bucketName)) {
                return;
            }
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("删除存储桶[{}]异常", bucketName);
            throw new BadMinioException("003", "删除存储桶异常", e);
        }
    }

    /**
     * 列出所有存储桶
     */
    public List<Bucket> listBuckets() {
        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            throw new BadMinioException("004", "获取多有bucket异常", e);
        }
    }


    /**
     * 列出某个存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public List<Item> listObjects(String bucketName) {
        return this.listObjects(bucketName, null, null, null);
    }

    /**
     * 列出某个存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @param prefix     对象名称的前缀
     * @return
     */
    public List<Item> listObjects(String bucketName, String prefix) {
        return this.listObjects(bucketName, prefix, null, null);
    }

    /**
     * 列出某个存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @param prefix     对象名称的前缀
     * @param recursive  是否递归查找，如果是false,就模拟文件夹结构查找。
     * @return
     */
    public List<Item> listObjects(String bucketName, String prefix, Boolean recursive) {
        return this.listObjects(bucketName, prefix, recursive, null);
    }

    /**
     * 列出某个存储桶中的所有对象
     *
     * @param bucketName  存储桶名称
     * @param prefix      对象名称的前缀
     * @param recursive   是否递归查找，如果是false,就模拟文件夹结构查找。
     * @param useVersion1 如果是true, 使用版本1 REST API
     * @return
     */
    public List<Item> listObjects(String bucketName, String prefix, Boolean recursive, Boolean useVersion1) {
        try {
            if (StringUtil.StringUtilsBylang3.isEmpty(bucketName)) {
                throw new BadMinioException("005", "列出存储桶中的所有对象时没有指定存储桶名称");
            }

            ListObjectsArgs.Builder builder = ListObjectsArgs.builder();
            builder.bucket(bucketName);

            if (StringUtil.StringUtilsBylang3.isNotEmpty(prefix)) {
                builder.prefix(prefix);
            }

            if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(recursive)) {
                builder.recursive(recursive);
            }

            if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(useVersion1)) {
                builder.useApiVersion1(useVersion1);
            }

            Iterable<Result<Item>> results = minioClient.listObjects(builder.build());
            List<Item> items = new ArrayList<>();
            if (results != null) {
                Iterator<Result<Item>> iterator = results.iterator();
                while (iterator.hasNext()) {
                    items.add(iterator.next().get());
                }
            }
            return items;
        } catch (Exception e) {
            log.error("列出存储桶[{}]中的所有对象异常", bucketName);
            throw new BadMinioException("006", "列出存储桶中的所有对象异常", e);
        }
    }

    /*=========================存储桶操作====================================*/


    /*=========================文件对象操作====================================*/

    /**
     * 以流的形式下载一个对象
     *
     * @param bucketName          存储桶名称
     * @param objectName          存储桶里的对象名称
     * @param httpServletResponse 请求的响应体
     * @return 返回文件信息
     */
    public GenericResponse getObject(String bucketName, String objectName
            , HttpServletResponse httpServletResponse) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                .object(objectName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = response.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                httpServletResponse.setCharacterEncoding("utf-8");
                //设置强制下载不打开
                //res.setContentType("application/force-download");
                httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + objectName);
                try (ServletOutputStream stream = httpServletResponse.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
            Headers headers = response.headers();
            String bucket = response.bucket();
            String object = response.object();
            String region = response.region();
            GenericResponse genericResponse = new GenericResponse(headers, bucket, object, region);
            log.debug("以流的形式下载一个对象，对象信息为:[{}]", JsonUtil.obj2String(genericResponse));
            return genericResponse;
        } catch (Exception e) {
            log.error("以流的形式下载一个对象[bucket:[{}]--------object:[{}]]异常", bucketName, objectName);
            throw new BadMinioException("007", "以流的形式下载一个对象异常", e);
        }
    }

    /**
     * 通过MultipartFile上传对象
     * 文件名称相同会覆盖
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param file       上传的文件
     * @return
     */
    public ObjectWriteResponse putObject(String bucketName, String objectName, MultipartFile file) {
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build();
            //文件名称相同会覆盖
            return minioClient.putObject(objectArgs);
        } catch (Exception e) {
            log.error("通过MultipartFile上传对象[bucket:[{}]--------object:[{}]]异常", bucketName, objectName);
            throw new BadMinioException("008", "通过MultipartFile上传对象异常", e);
        }
    }

    /**
     * 通过MultipartFile上传对象
     * 文件名称相同会覆盖
     *
     * @param bucketName  存储桶名称
     * @param objectName  存储桶里的对象名称
     * @param objectSize  要上传的stream的size
     * @param contentType 存储桶里的对象名称
     * @param inputStream Content type
     * @return
     */
    public ObjectWriteResponse putObject(String bucketName, String objectName,
                                         Long objectSize, String contentType, InputStream inputStream) {
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, objectSize, -1)
                    .contentType(contentType)
                    .build();
            return minioClient.putObject(objectArgs);
        } catch (Exception e) {
            log.error("通过MultipartFile上传对象[bucket:[{}]--------object:[{}]]异常", bucketName, objectName);
            throw new BadMinioException("009", "通过InputStream上传对象异常", e);
        }
    }

    /**
     * 通过MultipartFile上传对象
     * 文件名称相同会覆盖
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param filePach   要上传的文件路径
     * @return
     */
    public ObjectWriteResponse putObject(String bucketName, String objectName,
                                         String filePach) {
        try {
            File file = new File(filePach);
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(new FileInputStream(file), file.getTotalSpace(), -1)
                    .build();
            return minioClient.putObject(objectArgs);
        } catch (Exception e) {
            log.error("通过MultipartFile上传对象[bucket:[{}]--------object:[{}]]异常", bucketName, objectName);
            throw new BadMinioException("010", "通过FilePach上传对象异常", e);
        }
    }

    /**
     * 通过MultipartFile上传对象
     * 文件名称相同会覆盖
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param file       要上传的文件
     * @return
     */
    public ObjectWriteResponse putObject(String bucketName, String objectName,
                                         File file) {
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(new FileInputStream(file), file.getTotalSpace(), -1)
                    .build();
            return minioClient.putObject(objectArgs);
        } catch (Exception e) {
            log.error("通过MultipartFile上传对象[bucket:[{}]--------object:[{}]]异常", bucketName, objectName);
            throw new BadMinioException("011", "通过File上传对象异常", e);
        }
    }

    /**
     * 从指定bucketName中拷贝对象objectName到另外一个destBucketName的destObjectName
     *
     * @param bucketName     源存储桶名称
     * @param objectName     源存储桶中的源对象名称
     * @param destBucketName 目标存储桶名称
     * @param destObjectName 要创建的目标对象名称,如果为空，默认为源对象名称
     */
    public ObjectWriteResponse copyObject(String bucketName, String objectName,
                                          String destBucketName, String destObjectName) {
        try {
            CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder()
                    .bucket(destBucketName)
                    .object(destObjectName)
                    .source(CopySource.builder()
                            .bucket(bucketName)
                            .object(objectName).build()).build();
            return minioClient.copyObject(copyObjectArgs);
        } catch (Exception e) {
            log.error("从指定bucketName中拷贝对象objectName到另外一个destBucketName的destObjectName对象[bucket:[{}]---object:[{}]" +
                    "---destBucket:[{}]---destObject:[{}]]异常", bucketName, objectName, destBucketName, destObjectName);
            throw new BadMinioException("012", "copy存储对象异常", e);
        }
    }

    /**
     * 从指定bucketName中拷贝对象objectName到另外一个destBucketName的objectName对象
     *
     * @param bucketName     源存储桶名称
     * @param objectName     源存储桶中的源对象名称
     * @param destBucketName 目标存储桶名称
     */
    public ObjectWriteResponse copyObject(String bucketName, String objectName,
                                          String destBucketName) {
        try {
            CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder()
                    .bucket(destBucketName)
                    .object(objectName)
                    .source(CopySource.builder()
                            .bucket(bucketName)
                            .object(objectName).build()).build();
            return minioClient.copyObject(copyObjectArgs);
        } catch (Exception e) {
            log.error("从指定bucketName中拷贝对象objectName到另外一个destBucketName的objectName对象" +
                            "[bucket:[{}]---object:[{}]---destBucket:[{}]---destObject:[{}]]异常",
                    bucketName, objectName, destBucketName, objectName);
            throw new BadMinioException("013", "copy存储对象异常", e);
        }
    }


    /**
     * 删除一个对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return
     */
    public boolean removeObject(String bucketName, String objectName) {
        try {
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.removeObject(removeObjectArgs);
            return true;
        } catch (Exception e) {
            log.warn("删除一个对象[bucket:[{}]---object:[{}]]异常",
                    bucketName, objectName);
            return false;
        }
    }

    /**
     * 批量删除文件对象
     *
     * @param bucketName  存储桶名称
     * @param objectNameS 存储桶里的对象名称集合
     */
    public Collection<DeleteError> removeObjectS(String bucketName, Collection<String> objectNameS) {
        try {
            List<DeleteObject> deleteObjects = objectNameS.stream().map(DeleteObject::new).collect(Collectors.toList());
            RemoveObjectsArgs removeObjectsArgs
                    = RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build();
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
            Collection<DeleteError> deleteErrors = new ArrayList<>();
            Iterator<Result<DeleteError>> iterator = results.iterator();
            while (iterator.hasNext()) {
                deleteErrors.add(iterator.next().get());
            }
            return deleteErrors;
        } catch (Exception e) {
            log.error("批量删除对象[bucket:[{}]---object:[{}]]异常",
                    bucketName, JsonUtil.obj2String(objectNameS));
            throw new BadMinioException("017", "批量删除对象", e);
        }
    }

    /**
     * 获取对象的元数据
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return
     */
    public StatObjectResponse statObject(String bucketName, String objectName) {
        try {
            StatObjectArgs statObjectArgs = StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            return minioClient.statObject(statObjectArgs);
        } catch (Exception e) {
            log.error("获取对象的元数据[bucket:[{}]---object:[{}]]异常",
                    bucketName, objectName);
            throw new BadMinioException("014", "获取对象的元数据", e);
        }
    }

    /*=========================文件对象操作====================================*/


    /*=========================Presigned操作====================================*/

    /**
     * 生成一个不需要秘钥的外链
     * <p>
     * 浏览器/移动端的客户端可以用这个URL进行下载、上传，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间。
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param method     请求方式
     * @param expiry     过期时间（单位秒） 最大为7天 超过7天则默认最大值
     * @return
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, Method method, Integer expiry) {
        try {
            GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(expiry)
                    .build();
            return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
        } catch (Exception e) {
            log.error("生成一个不需要秘钥的外链[bucket:[{}]---object:[{}]]异常",
                    bucketName, objectName);
            throw new BadMinioException("015", "生成一个不需要秘钥的外链异常", e);
        }
    }

    /**
     * 生成一个不需要秘钥的外链
     * <p>
     * 浏览器/移动端的客户端可以用这个URL进行下载、上传，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间。
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param method     请求方式
     * @param timeUnit   过期时间单位
     * @param duration   过期时间  最大为7天 超过7天则默认最大值
     * @return
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, Method method, TimeUnit timeUnit, int duration) {
        try {
            GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(duration, timeUnit)
                    .build();
            return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
        } catch (Exception e) {
            log.error("生成一个不需要秘钥的外链[bucket:[{}]---object:[{}]]异常",
                    bucketName, objectName);
            throw new BadMinioException("016", "生成一个不需要秘钥的外链异常", e);
        }
    }


    /**
     * 获取访问对象的外链地址
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param expiry     过期时间（单位秒） 最大为7天 超过7天则默认最大值
     * @return viewUrl
     */
    public String getObjectUrl(String bucketName, String objectName, Integer expiry) {
        return this.getPresignedObjectUrl(bucketName, objectName, Method.GET, expiry);
    }

    /**
     * 获取访问对象的外链地址
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param timeUnit   过期时间单位
     * @param duration   过期时间  最大为7天 超过7天则默认最大值
     * @return viewUrl
     */
    public String getObjectUrl(String bucketName, String objectName, TimeUnit timeUnit, int duration) {
        return this.getPresignedObjectUrl(bucketName, objectName, Method.GET, timeUnit, duration);
    }

    /**
     * 创建上传文件对象的外链
     *
     * @param bucketName 存储桶名称
     * @param objectName 欲上传文件对象的名称
     * @param expiry     过期时间（单位秒） 最大为7天 超过7天则默认最大值
     * @return uploadUrl
     */
    public String createUploadUrl(String bucketName, String objectName, Integer expiry) {
        return this.getPresignedObjectUrl(bucketName, objectName, Method.PUT, expiry);
    }

    /**
     * 创建上传文件对象的外链
     *
     * @param bucketName 存储桶名称
     * @param objectName 欲上传文件对象的名称
     * @param timeUnit   过期时间单位
     * @param duration   过期时间  最大为7天 超过7天则默认最大值
     * @return uploadUrl
     */
    public String createUploadUrl(String bucketName, String objectName, TimeUnit timeUnit, int duration) {
        return this.getPresignedObjectUrl(bucketName, objectName, Method.PUT, timeUnit, duration);
    }

    /**
     * 创建指定分片上传对象的外链,过期时间为七天
     *
     * @param bucketName 存储桶名称
     * @param objectName 欲上传文件对象的名称
     * @param objectMD5  欲上传分片文件主文件的MD5
     * @param partNumber 分片序号
     * @return
     */
    public String createUploadChunkUrl(String bucketName, String objectName, String objectMD5, Integer partNumber) {
        return this.createUploadUrl(bucketName,
                generatorChunkNameWithPartNumber(objectName, objectMD5, partNumber),
                TimeUnit.DAYS, 7);
    }

    /**
     * 指定分配大小创建上传对象的外链,过期时间为七天
     *
     * @param bucketName 存储桶名称
     * @param objectName 欲上传文件对象的名称
     * @param objectMD5  欲上传分片文件主文件的MD5
     * @param partSize   分片大小
     * @return
     */
    public List<String> createUploadChunkUrlList(String bucketName, String objectName, String objectMD5, Integer partSize) {
        List<String> urlList = new ArrayList<>();
        for (int i = 1; i <= partSize; i++) {
            urlList.add(this.createUploadChunkUrl(bucketName, objectName, objectMD5, i));
        }
        return urlList;

    }

    /**
     * 合并分片文件成对象文件
     *
     * @param chunkBucKetName   分片文件所在存储桶名称
     * @param chunkNames        分片文件名称集合
     * @param composeBucketName 合并后的对象文件存储的存储桶名称
     * @param objectName        合并后的对象文件名称
     * @return true/false
     */
    public void composeObject(String chunkBucKetName, List<String> chunkNames, String composeBucketName, String objectName) {
        try {
            List<ComposeSource> sourceObjectList = new ArrayList<>(chunkNames.size());
            for (String chunk : chunkNames) {
                sourceObjectList.add(ComposeSource.builder()
                        .bucket(chunkBucKetName)
                        .object(chunk)
                        .build());
            }
            ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
                    .bucket(composeBucketName)
                    .object(objectName)
                    .sources(sourceObjectList)
                    .build();
            minioClient.composeObject(composeObjectArgs);

            // 合并文件后删除分块
            this.removeObjectS(chunkBucKetName, chunkNames);
        } catch (Exception e) {
            log.error("合并分片文件成对象文件[chunkBucKet:[{}]---chunkNames:[{}]" +
                            "---composeBucket:[{}]---objectName:[{}]]异常",
                    chunkBucKetName, JsonUtil.obj2String(chunkNames), composeBucketName, objectName);
            throw new BadMinioException("022", "合并分片文件成对象文件异常", e);
        }
    }


    /*=========================Presigned操作====================================*/


    /*=========================存储桶策略====================================*/

    /**
     * 获得指定对象前缀的存储桶策略。
     *
     * @param bucketName 存储桶名称
     * @param region     区域
     * @return
     */
    public String getBucketPolicy(String bucketName, String region) {

        try {
            GetBucketPolicyArgs getBucketPolicyArgs = GetBucketPolicyArgs.builder()
                    .bucket(bucketName)
                    .region(region)
                    .build();
            return minioClient.getBucketPolicy(getBucketPolicyArgs);
        } catch (Exception e) {
            log.error("获取对象的元数据[bucket:[{}]---region:[{}]]异常",
                    bucketName, region);
            throw new BadMinioException("019", "获取对象的元数据", e);
        }

    }

    /**
     * 给一个存储桶+对象前缀设置策略。
     *
     * @param bucketName 存储桶名称
     * @param region     区域
     * @param config
     */
    public void setBucketPolicy(String bucketName, String region, String config) {
        try {
            SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                    .bucket(bucketName)
                    .region(region)
                    .config(config)
                    .build();
            minioClient.setBucketPolicy(setBucketPolicyArgs);
        } catch (Exception e) {
            log.error("获取对象的元数据[bucket:[{}]---region:[{}]---config:[{}]]异常",
                    bucketName, region, config);
            throw new BadMinioException("020", "获取对象的元数据", e);
        }
    }

    /*=========================存储桶策略====================================*/


    /**
     * 拼接文件对象名称和文件对象MD5生成新的MD5
     *
     * @param objectName 欲上传文件对象的名称
     * @param objectMD5  欲上传文件对象的MD5
     * @return
     */
    public static String generatorChunkName(String objectName, String objectMD5) {
        if (StringUtil.StringUtilsBylang3.isEmpty(objectName)
                || StringUtil.StringUtilsBylang3.isEmpty(objectMD5)) {
            throw new BadMinioException("018", "拼接文件对象名称和文件对象MD5生成新的MD5异常");
        }
        return SecureUtil.md5(objectName.concat(objectMD5));

    }

    /**
     * 拼接文件对象名称和文件对象MD5生成新的MD5加上分配序号生成的外联名称
     *
     * @param objectName 欲上传文件对象的名称
     * @param objectMD5  欲上传文件对象的MD5
     * @param partNumber 分片序号
     * @return
     */
    public static String generatorChunkNameWithPartNumber(String objectName, String objectMD5, Integer partNumber) {
        return generatorChunkName(objectName, objectMD5).concat("/").concat(String.valueOf(partNumber)).concat(".chunk");
    }

    /**
     * 拼接文件对象名称和文件对象MD5生成新的MD5加上分配序号生成的外联名称
     *
     * @param chunkName  拼接文件对象名称和文件对象MD5生成新的MD5
     * @param partNumber 分片序号
     * @return
     */
    public static String generatorChunkNameWithPartNumber(String chunkName, Integer partNumber) {
        if (StringUtil.StringUtilsBylang3.isEmpty(chunkName)) {
            throw new BadMinioException("021", "拼接文件对象名称和文件对象MD5生成新的MD5值为空");
        }

        return chunkName.concat("/").concat(String.valueOf(partNumber)).concat(".chunk");
    }

}